package com.leyou.filter;

import com.leyou.auth.utils.JwtUtils;
import com.leyou.common.utils.CookieUtils;
import com.leyou.config.JwtProperties;
import com.leyou.config.filterProperties;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * 判断登录的过滤器
 */
@Component
@EnableConfigurationProperties({JwtProperties.class, filterProperties.class})
public class LoginFilter extends ZuulFilter {

    @Autowired
    private JwtProperties pro;

    @Autowired
    private filterProperties filterProperties;

    private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class);


    /**
     * filterType：返回一个字符串代表过滤器的类型，在zuul中定义了四种不同生命周期的过滤器类型，具体如下：
     *
     * pre：可以在请求被路由之前调用
     * route：在路由请求时候被调用
     * post：在route和error过滤器之后被调用
     * error：处理请求时发生错误时被调用
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 定义过滤器的优先级，如果有多个的话根据优先级的顺序执行，倒叙排，执行下个过滤器是4
     * @return
     */
    @Override
    public int filterOrder() {
        return 5;
    }

    /**
     * 返回一个boolean类型来判断该过滤器是否要执行，所以通过此函数可实现过滤器的开关。
     * 在上例中，我们直接返回true，所以该过滤器总是生效
     * @return
     */
    @Override
    public boolean shouldFilter() {
        // 获取上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        // 获取request
        HttpServletRequest req = ctx.getRequest();
        // 获取路径
        String requestURI = req.getRequestURI();
        // 判断配置文件里的白名单
        // 遍历允许访问的路径
        for (String path : this.filterProperties.getAllowPaths()) {
            // 然后判断是否是符合，如果包含，不拦截直接放行。。
            if (requestURI.startsWith(path)) {
                return false;
            }
        }
        // 不存在返回true拦截，执行下面的run方法拦截规则
        return true;
    }

    /**
     * 过滤器的具体逻辑。需要注意，这里我们通过context.setSendZuulResponse(false)令zuul过滤该请求，
     * 不对其进行路由，然后通过context.setResponseStatusCode(403)设置了其返回的错误码
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        // 获取上下文
        RequestContext context = RequestContext.getCurrentContext();

        // 获取request
        HttpServletRequest request = context.getRequest();
        // 获取token
        String token = CookieUtils.getCookieValue(request, this.pro.getCookieName());

        try {
            // 解密。如果没问题说明效验通过什么都不做，放行m
            JwtUtils.getInfoFromToken(token, this.pro.getPublicKey());
        } catch (Exception e) {
            // 效验出现异常返回403
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
            logger.error("非法访问，未登录，地址：{}", request.getRemoteHost(), e);
        }
        return null;
    }
}
